package net.lanelife.casino.caipiao.game;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.lanelife.casino.caipiao.entity.DigitalSelector;
import net.lanelife.casino.caipiao.entity.Selector;
import net.lanelife.casino.common.utils.ArrayUtils;
import net.lanelife.casino.common.utils.MathUtils;
import net.lanelife.casino.common.utils.StringUtils;

import org.raistlic.common.permutation.Combination;

public class GameLogic {
	
	/**
	 * 计算注数
	 * @param content          投注内容			- 0,1,2||1,2,3||
	 * @param position         投注位置			- 0,2
	 * @param play             玩法名称			- 定位胆
	 * @param method           玩法算法			- DWD
	 * @param nonrepeatability 不可重复性			- SINGLE
	 * @param selectorType     选号方式			- DIGITAL
	 * @param selectors        选号器列表			- 万位、千位、百位、十位、个位
	 * @param separator        分割符			- |
	 * @param minchosen        最少位置个数		- 1
	 * @param maxchosen        最多位置个数		- 5
	 * @param isArbitrarily    是否任选			- false
	 * @return 注数
	 * @throws Exception
	 */
	@SuppressWarnings("incomplete-switch")
	public static int count(String content, String position, String play, GameMethod method, Nonrepeatability nonrepeatability, SelectorType selectorType, List<Selector> selectors, String separator, int minchosen, int maxchosen, boolean isArbitrarily) throws Exception {
		
		int        count    = 0; 						                           // 注数
		Integer    digits[] = StringUtils.toIntegerArray(position, ",");           // 选择的位
		String     suffix   = method.name().substring(method.name().length() - 1); // 算法枚举后缀，即最后一个数字，例如：ZXZH5，suffix = 5
		int        star     = Integer.valueOf(suffix);                             // 几星
		
		
		if (StringUtils.isBlank(content)) {
			throw new Exception("投注内容不能为空"); 																		// 异常：投注内容为空
		}
		
		
		// ======================================================== 位式 ======================================================== //

		if (selectorType == SelectorType.DIGITAL) {
			
			List<DigitalSelector> ds     = new ArrayList<DigitalSelector>();                    // 位选择器列表
			String[][]            data   = new String[selectors.size()][];                      // 号码二维数组，一维：位；二维：号码
			int                   temp   = 1;                                                   // 临时注数变量
			
			String[] nums = content.split(separator, -1);            // 分割位，包含空元素
			int ilen = StringUtils.split(content, separator).length; // 有包含号码的位个数，即不包含空元素的数组个数，或称有效位数
			
			// 校验位数，例如【后三直选】只有【百十个】3位，如果数组元素个数不等于3个则是非法注单
			if (nums.length != selectors.size()) {
				throw new Exception("投注内容有误, 位数不正确");	 														// 异常：按位分割后的数组元素个数不等于位选择器个数
			}
			// 校验有效位数
			if (ilen < minchosen) {
				throw new Exception("投注内容有误, " + play + " 至少需要选择" + minchosen + "个位置"); 		// 异常：小于最少位置
			}
			if (ilen > maxchosen) {
				throw new Exception("投注内容有误, " + play + " 最多只能选择" + maxchosen + "个位置"); 		// 异常：大于最多位置
			}
			
			for (int i = 0; i < selectors.size(); i++) {
				DigitalSelector selector = (DigitalSelector) selectors.get(i);
				String[] nos = StringUtils.split(nums[i], selector.getSeparator()); // 分割号码，不包含空元素，例如【定位胆】
				// 验证有效号码个数
				if (nos.length < selector.getMinchosen()) {
					throw new Exception(selector.getTitle() + " 至少需要选择" + selector.getMinchosen() + "个号码"); 		// 异常：少于最少号码个数
				}
				if (nos.length > selector.getMaxchosen()) {
					throw new Exception(selector.getTitle() + " 最多只能选择" + selector.getMaxchosen() + "个号码"); 		// 异常：大于最多号码个数
				}
				// 验证号码有效性，如果该位无号码则不验证，例如【定位胆】不用每位都选号码
				if (!"".equals(nums[i])) {
					String[] verif = nums[i].split(selector.getSeparator(), -1); // 待验证的号码数组
					String[] allow = selector.getNo().split("\\|", -1);          // 允许的号码数组
					for (String no : verif) {
						if (!ArrayUtils.contains(allow, no)) {
							throw new Exception("投注内容有误, 所投号码["+no+"]不在允许号码范围内");									// 异常：所投号码不在允许号码范围内
						}
					}
				}
				ds.add(selector);
				data[i] = nos;
			}
			
			// 验证号码重复性
			switch (nonrepeatability) {
			case SINGLE: // 单位号码不能重复
				for (String[] nos : data) {
					if (ArrayUtils.duplicate(nos)) {
						throw new Exception("投注内容有误, 单个位置的号码不能重复");
					}
				}
				break;
			case ALL: // 完全不能重复
				String[] all = null;
				for (String[] nos : data) {
					all = (String[]) ArrayUtils.addAll(all, nos);
				}
				if (ArrayUtils.duplicate(all)) {
					throw new Exception("投注内容有误, 号码不能重复");
				}
				break;
			}
			
			
			switch (method) {
			
			// ----------------------------------------- 直选玩法 ----------------------------------------- //
			case ZX5:   // 五星直选
			case ZX4:   // 四星直选
			case ZX3:   // 三星直选
			case ZX2:   // 二星直选
			case BDW1:  // 不定位一码
			case YFFS:  // 一帆风顺
			case HSCS:  // 好事成双
			case SXBX:  // 四星报喜
			case SJFC:  // 四季发财
			case DXDS:  // 大小单双
			case HZWS3: // 三星和值尾数
			case TSH3:  // 三星特殊号
			{ 
				for (int i = 0; i < data.length; i++) { // 循环每位
					if (data[i].length == 0) { // 如果某位号码为空，则注数为0
						temp = 0;
						break;
					}
					temp *= data[i].length;
				}
				count = temp;
				break;
			}
			
			// ----------------------------------------- 直选组合 ----------------------------------------- //
			case ZXZH5: // 五星直选组合
			case ZXZH4: // 四星直选组合
			case ZXZH3: // 三星直选组合
			{
				for (int i = 0; i < data.length; i++) { // 循环每位
					if (data[i].length == 0) { // 如果某位号码为空，则注数为0
						temp = 0;
						break;
					}
					temp *= data[i].length;
				}
				count = temp * star;
				break;
			}
			
			// ----------------------------------------- 五星组选 ----------------------------------------- //
			case WXZU120: // 五星组选120
			{
				int len = data[0].length;
				if (len > 4) {
					count += MathUtils.combination(len, 5);
				}
				break;
			}
			case WXZU60: // 五星组选60
			case WXZU30: // 五星组选30
			case WXZU20: // 五星组选20
			case WXZU10: // 五星组选10
			case WXZU5:  // 五星组选5
			{
				String a[] = data[0]; // 二重号、二重号、三重号、三重号、四重号
				String b[] = data[1]; // 　单号、　单号、　单号、二重号、　单号
				int mina = ds.get(0).getMinchosen();
				int minb = ds.get(1).getMinchosen();
				if (a.length >= mina && b.length >= minb) {
					int h = ArrayUtils.intersect(a, b).length; // 交集个数
					temp = MathUtils.combination(a.length, mina) * MathUtils.combination(b.length, minb);
					if (h > 0) {
						switch (method) {
						case WXZU60: // 五星组选60
						{
							temp -= MathUtils.combination(h, 1) * MathUtils.combination(b.length - 1, 2);
							break;
						}
						case WXZU30: // 五星组选30
						{
							temp -= MathUtils.combination(h, 2) * MathUtils.combination(2, 1);
							if (a.length - h > 0) {
								temp -= MathUtils.combination(h, 1) * MathUtils.combination(a.length - h, 1);
							}
							break;
						}
						case WXZU20: // 五星组选20
						{
							temp -= MathUtils.combination(h, 1) * MathUtils.combination(b.length - 1, 1);
							break;
						}
						case WXZU10: // 五星组选10
						case WXZU5:  // 五星组选5
						{
							temp -= MathUtils.combination(h, 1);
							break;
						}
						}
					}
					count += temp;
				}
				break;
			}
			
			// ----------------------------------------- 四星组选 ----------------------------------------- //
			case SXZU24: // 四星组选24
			case SXZU6:  // 四星组选6
			{
				star = 4;
				int len = data[0].length;
				int min = ds.get(0).getMinchosen();
				if (len >= min) {
					count += MathUtils.combination(len, min);
				}
				break;
			}
			case SXZU12: // 四星组选12
			case SXZU4:  // 四星组选4
			{
				star = 4;
				String a[] = data[0]; // 二重号、三重号
				String b[] = data[1]; // 　单号、　单号
				int mina = ds.get(0).getMinchosen();
				int minb = ds.get(1).getMinchosen();
				if (a.length >= mina && b.length >= minb) {
					int h = ArrayUtils.intersect(a, b).length; // 交集个数
					temp = MathUtils.combination(a.length, mina) * MathUtils.combination(b.length, minb);
					if (h > 0) {
						switch (method) {
						case SXZU12: // 四星组选12
						{
							temp -= MathUtils.combination(h, 1) * MathUtils.combination(b.length - 1, 1);
							break;
						}
						case SXZU4:  // 四星组选4
						{
							temp -= MathUtils.combination(h, 1);
							break;
						}
						}
					}
					count += temp;
				}
				break;
			}
			
			// ----------------------------------------- 跨度和值 ----------------------------------------- //
			case ZXKD3: // 三星直选跨度
			case ZXKD2: // 二星直选跨度
			case ZXHZ3: // 三星直选和值
			case ZXHZ2: // 二星直选和值
			case ZUHZ3: // 三星组选和值
			case ZUHZ2: // 二星组选和值
			{
				Map<String, Integer> d = new HashMap<String, Integer>(); // 和值、跨度的固定注数
				switch (method) {
				case ZXKD3: // 三星直选跨度
				{
					d.put("0", 10);  d.put("1", 54);  d.put("2", 96);  d.put("3", 126); d.put("4", 144);
					d.put("5", 150); d.put("6", 144); d.put("7", 126); d.put("8", 96);  d.put("9", 54);
					break;
				}
				case ZXKD2: // 二星直选跨度
				{
					d.put("0", 10); d.put("1", 18); d.put("2", 16); d.put("3", 14); d.put("4", 12);
					d.put("5", 10); d.put("6", 8);  d.put("7", 6);  d.put("8", 4);  d.put("9", 2);
					break;
				}
				case ZXHZ3: // 三星直选和值
				{
					d.put("0", 1);   d.put("1", 3);   d.put("2", 6);   d.put("3", 10);  d.put("4", 15);
					d.put("5", 21);  d.put("6", 28);  d.put("7", 36);  d.put("8", 45);  d.put("9", 55);
					d.put("10", 63); d.put("11", 69); d.put("12", 73); d.put("13", 75); d.put("14", 75);
					d.put("15", 73); d.put("16", 69); d.put("17", 63); d.put("18", 55); d.put("19", 45);
					d.put("20", 36); d.put("21", 28); d.put("22", 21); d.put("23", 15); d.put("24", 10);
					d.put("25", 6);  d.put("26", 3);  d.put("27", 1);
					break;
				} 
				case ZXHZ2: // 二星直选和值
				{
					d.put("0", 1);  d.put("1", 2);  d.put("2", 3);  d.put("3", 4);  d.put("4", 5);
					d.put("5", 6);  d.put("6", 7);  d.put("7", 8);  d.put("8", 9);  d.put("9", 10);
					d.put("10", 9); d.put("11", 8); d.put("12", 7); d.put("13", 6); d.put("14", 5);
					d.put("15", 4); d.put("16", 3); d.put("17", 2); d.put("18", 1);
					break;
				} 
				case ZUHZ3: // 三星组选和值
				{
					d.put("1", 1);   d.put("2", 2);   d.put("3", 2);   d.put("4", 4);   d.put("5", 5);  
					d.put("6", 6);   d.put("7", 8);   d.put("8", 10);  d.put("9", 11);  d.put("10", 13); 
					d.put("11", 14); d.put("12", 14); d.put("13", 15); d.put("14", 15); d.put("15", 14); 
					d.put("16", 14); d.put("17", 13); d.put("18", 11); d.put("19", 10); d.put("20", 8); 
					d.put("21", 6);  d.put("22", 5);  d.put("23", 4);  d.put("24", 2);  d.put("25", 2);  
					d.put("26", 1);
					break;
				} 
				case ZUHZ2: // 二星组选和值
				{
					d.put("0", 0);  d.put("1", 1);  d.put("2", 1);  d.put("3", 2);  d.put("4", 2);
					d.put("5", 3);  d.put("6", 3);  d.put("7", 4);  d.put("8", 4);  d.put("9", 5);
					d.put("10", 4); d.put("11", 4); d.put("12", 3); d.put("13", 3); d.put("14", 2);
					d.put("15", 2); d.put("16", 1); d.put("17", 1); d.put("18", 0);
					break;
				} 
				}
				for (String nos[] : data) {
					for (String n : nos) {
						count += d.get(n);
					}
				}
				break;
			}
			
			// ----------------------------------------- 组六组三组二不定位二码 ----------------------------------------- //
			case ZU6: // 组六
			{
				star = 3;
				for (String[] nos : data) {
					int len = nos.length;
					if (len > 2) {
						count += len * (len - 1) * (len - 2) / 6;
					}
				}
				break;
			}
			case ZU3: // 组三
			{
				star = 3;
				for (String[] nos : data) {
					int len = nos.length;
					if (len > 1) {
						count += len * (len - 1);
					}
				}
				break;
			}
			case BDW2: // 不定位二码
			case ZU2:  // 组二
			{
				star = 2;
				for (String[] nos : data) {
					int len = nos.length;
					if (len > 1) {
						count += len * (len - 1) / 2;
					}
				}
				break;
			}
			
			// ----------------------------------------- 定位胆 ----------------------------------------- //
			case DWD: // 定位胆
			{
				for (String[] nos : data) {
					count += nos.length;
				}
				break;
			}
			
			// ----------------------------------------- 不定位三码 ----------------------------------------- //
			case BDW3: // 不定位三码
			{
				for (String[] nos : data) {
					int len = nos.length;
					if (len > 2) {
						count += MathUtils.combination(len, 3);
					}
				}
				break;
			}
			
			// ----------------------------------------- 组选包胆 ----------------------------------------- //
			case ZUBD3: // 三星组选包胆
			{
				count = data[0].length * 54;
				break;
			}
			case ZUBD2: // 二星组选包胆
			{
				count = data[0].length * 9;
				break;
			}
			
			// ----------------------------------------- 任选直选 ----------------------------------------- //
			case RXZX4:
			case RXZX3:
			case RXZX2:
			{
				List<Integer> pos = new ArrayList<>();
				for (int i = 0; i < data.length; i++) {
					if (data[i].length > 0) {
						pos.add(i);
					}
				}
				Combination<Integer> p = Combination.of(pos, star);
				for (List<Integer> c : p) {
					int len = c.size();
					temp = 1;
					for (int i = 0; i< len; i++) {
						temp *= data[c.get(i)].length;
					}
					count += temp;
				}
				break;
			}
			
			
			// ----------------------------------------- 十一选五 ----------------------------------------- //
			case LTZX3: // 十一选五三星直选
			{
				String a[] = data[0];
				String b[] = data[1];
				String c[] = data[2];
				if (a.length > 0 && b.length > 0 && c.length > 0) {
					for (int i = 0; i < a.length; i++) {
						for (int j = 0; j < b.length; j++) {
							for (int k = 0; k < c.length; k++) {
								if (!a[i].equals(b[j]) && !a[i].equals(c[k]) && !b[j].equals(c[k])) {
									count++;
								}
							}
						}
					}
				}
				break;
			}
			case LTZX2: // 十一选五二星直选
			{
				String a[] = data[0];
				String b[] = data[1];
				if (a.length > 0 && b.length > 0) {
					int h = ArrayUtils.intersect(a, b).length;
					count = a.length * b.length - h;
				}
				break;
			}
			case LTZU3:  // 十一选五三星组选
			case LTZU2:  // 十一选五二星组选
			case LTBDW1: // 十一选五不定位一码
			case LTCZW:  // 十一选五猜中位
			case LTRX1:  // 十一选五任选一中一
			case LTRX2:  // 十一选五任选二中二
			case LTRX3:  // 十一选五任选三中三
			case LTRX4:  // 十一选五任选四中四
			case LTRX5:  // 十一选五任选五中五
			case LTRX6:  // 十一选五任选六中五
			case LTRX7:  // 十一选五任选七中五
			case LTRX8:  // 十一选五任选八中五
			{
				int len = data[0].length;
				int min = ds.get(0).getMinchosen();
				if (len >= min) {
					count += MathUtils.combination(len, min);
				}
				break;
			}
			case LTDWD: // 十一选五定位胆
			case LTDDS: // 十一选五定单双
			{
				for (String[] nos : data) {
					count += nos.length;
				}
				break;
			}
			case LTZUDT3: // 十一选五三星组选胆拖
			case LTZUDT2: // 十一选五二星组选胆拖
			case LTRXDT2: // 十一选五任选胆拖二中二
			case LTRXDT3: // 十一选五任选胆拖三中三
			case LTRXDT4: // 十一选五任选胆拖四中四
			case LTRXDT5: // 十一选五任选胆拖五中五
			case LTRXDT6: // 十一选五任选胆拖六中五
			case LTRXDT7: // 十一选五任选胆拖七中五
			case LTRXDT8: // 十一选五任选胆拖八中五
			{
				int danlen = data[0].length;
				int tuolen = data[1].length;
				int sellen = Integer.parseInt(suffix);
				if (danlen < 1 || tuolen < 1 || danlen >= sellen) {
					count = 0;
				} else {
					count = MathUtils.combination(tuolen, sellen - danlen);
				}
				break;
			}
			
			
			// ----------------------------------------- 快乐八 ----------------------------------------- //
			case KLBRX1:
			case KLBRX2:
			case KLBRX3:
			case KLBRX4:
			case KLBRX5:
			case KLBRX6:
			case KLBRX7:
			{
				int len = data[0].length;
				int min = ds.get(0).getMinchosen();
				if (len >= min) {
					count += MathUtils.combination(len, min);
				}
				break;
			}
			case KLBSXP:
			case KLBJOP:
			case KLBZHZDXDS:
			{
				for (int i = 0; i < data.length; i++) { // 循环每位
					if (data[i].length == 0) { // 如果某位号码为空，则注数为0
						temp = 0;
						break;
					}
					temp *= data[i].length;
				}
				count = temp;
				break;
			}
			
			// ----------------------------------------- PK10 ----------------------------------------- //
			case PKZX3: // PK10三星直选
			{
				String a[] = data[0];
				String b[] = data[1];
				String c[] = data[2];
				if (a.length > 0 && b.length > 0 && c.length > 0) {
					for (int i = 0; i < a.length; i++) {
						for (int j = 0; j < b.length; j++) {
							for (int k = 0; k < c.length; k++) {
								if (!a[i].equals(b[j]) && !a[i].equals(c[k]) && !b[j].equals(c[k])) {
									count++;
								}
							}
						}
					}
				}
				break;
			}
			case PKZX2: // PK10二星直选
			{
				String a[] = data[0];
				String b[] = data[1];
				if (a.length > 0 && b.length > 0) {
					int h = ArrayUtils.intersect(a, b).length;
					count = a.length * b.length - h;
				}
				break;
			}
			case PKZX1: // PK10一星直选
			{
				count = data[0].length;
				break;
			}
			case PKDWD: // PK10定位胆
			{
				for (String[] nos : data) {
					count += nos.length;
				}
				break;
			}
			
			default:
				throw new Exception("玩法不存在"); // 未找到算法
			}
			
		}
		
		// ======================================================== 文本 ======================================================== //
		
		if (selectorType == SelectorType.TEXT) {
			String[] data = content.split(separator, -1);
			Set<String> correctNums = new HashSet<String>(); // 正确号码集合
			Set<String> wrongNums = new HashSet<String>();   // 错误号码集合
			switch (method) {
			case ZX5:   // 五星直选
			case ZX4:   // 四星直选
			case ZX3:   // 三星直选
			case ZX2:   // 二星直选
			case RXZX4: // 任四直选
			case RXZX3: // 任三直选
			case RXZX2: // 任二直选
			{
				validate(correctNums, wrongNums, data, star, 1, false, null);
				break;
			}
			case ZU3: // 三星组三
			{
				star = 3;
				validate(correctNums, wrongNums, data, star, 1, true, new ZU3Validator());
				break;
			}
			case ZU6: // 三星组六
			{
				star = 3;
				validate(correctNums, wrongNums, data, star, 1, true, new ZU6Validator());
				break;
			}
			case ZU2:   // 二星组二
			case ZUHH3: // 三星组选混合
			{
				validate(correctNums, wrongNums, data, star, 1, true, new ZUHHValidator());
				break;
			}
			
			// ----------------------------------------- 十一选五 ----------------------------------------- //
			case LTZX3: // 十一选五三星直选
			case LTZX2: // 十一选五二星直选
			case LTRX1: // 十一选五任选一中一
			{
				validate(correctNums, wrongNums, data, star * 2, 2, false, new LTDSValidator());
				break;
			}
			case LTZU3: // 十一选五三星组选
			case LTZU2: // 十一选五二星组选
			case LTRX2: // 十一选五任选二中二
			case LTRX3: // 十一选五任选三中三
			case LTRX4: // 十一选五任选四中四
			case LTRX5: // 十一选五任选五中五
			case LTRX6: // 十一选五任选六中五
			case LTRX7: // 十一选五任选七中五
			case LTRX8: // 十一选五任选八中五
			{
				validate(correctNums, wrongNums, data, star * 2, 2, true, new LTDSValidator());
				break;
			}
			
			
			// ----------------------------------------- PK10 ----------------------------------------- //
			case PKZX3:
			case PKZX2:
			{
				validate(correctNums, wrongNums, data, star * 2, 2, false, new PKDSValidator());
				break;
			}
			
			default:
				throw new Exception("玩法不存在"); // 未找到算法
			}
			
			if (!wrongNums.isEmpty()) {
				throw new Exception("投注内容有误，以下号码重复或不符合规则：" + wrongNums);
			} else {
				count = correctNums.size();
			}
		}
		
		if (isArbitrarily) { // 任选玩法
			count *= digits.length == 0 ? 0 : MathUtils.combination(digits.length, star);
		}
		
		if (count <= 0) {
			throw new Exception("投注内容有误，注数小于等于0");
		}
		
		return count;
	}
	
	/**
	 * 验证号码
	 * @param correctNums 正确号码集合
	 * @param wrongNums   错误号码集合
	 * @param data        要验证的号码数组
	 * @param length      每注号码长度
	 * @param partition   单个号码长度
	 * @param sort        是否排序单注号码
	 * @param validator   验证器
	 */
	private static void validate(Set<String> correctNums, Set<String> wrongNums, String[] data, int length, int partition, boolean sort, Validator validator) {
		String nums;
		for (String n : data) {
			nums = sort ? sort(n, partition) : n;
			if (correctNums.contains(nums)) {
				wrongNums.add(n); // 重复号码
			} else if (!n.matches("^[0-9]{" + length + "}$")) {
				wrongNums.add(n); // 错误号码
			} else if (validator != null && !validator.validate(n, length)) {
				wrongNums.add(n); // 错误号码
			} else {
				correctNums.add(nums); // 正确号码
			}
		}
	}
	
	/**
	 * 排序单注号码
	 * @param nums      单注号码
	 * @param partition 单个号码长度
	 * @return
	 */
	private static String sort(String nums, int partition) {
		List<String> parts = StringUtils.partition(nums, partition);
		Object[] nos = parts.toArray();
		Arrays.sort(nos);
		return ArrayUtils.join(nos, "");
	}
}



/**
 * 验证器接口
 * @author Lane
 *
 */
interface Validator {
	boolean validate(String nums, int length);
}

/**
 * 组三验证器
 * @author Lane
 *
 */
class ZU3Validator implements Validator {
	@Override
	public boolean validate(String nums, int length) {
		if (length != 3) {
			return false;
		}
		String first = nums.substring(0, 1);
		String second = nums.substring(1, 2);
		String third = nums.substring(2, 3);
		if (first.equals(second) && second.equals(third)) {
			return false;
		}
		if (first.equals(second) || first.equals(third) || second.equals(third)) {
			return true;
		}
		return false;
	}
}

/**
 * 组六验证器
 * @author Lane
 *
 */
class ZU6Validator implements Validator {
	@Override
	public boolean validate(String nums, int length) {
		if (length != 3) {
			return false;
		}
		String first = nums.substring(0, 1);
		String second = nums.substring(1, 2);
		String third = nums.substring(2, 3);
		if (first.equals(second) || first.equals(third) || second.equals(third)) {
			return false;
		} else {
			return true;
		}
	}
}

/**
 * 组选混合验证器
 * @author Lane
 *
 */
class ZUHHValidator implements Validator {
	@Override
	public boolean validate(String nums, int length) {
		String array[];
		if (length == 2) {
			array = new String[]{"00", "11", "22", "33", "44", "55", "66", "77", "88", "99"};
		} else {
			array = new String[]{"000", "111", "222", "333", "444", "555", "666", "777", "888", "999"};
		}
		if (ArrayUtils.contains(array, nums)) {
			return false;
		} else {
			return true;
		}
	}
}

/**
 * 十一选五单式验证器
 * @author Lane
 *
 */
class LTDSValidator implements Validator {
	@Override
	public boolean validate(String nums, int length) {
		List<String> nos = StringUtils.partition(nums, 2);
		int len = nos.size();
		for (int i = 0; i < len; i++) {
			if (Integer.parseInt(nos.get(i)) > 11 || Integer.parseInt(nos.get(i)) < 1) {
				return false;
			}
			for (int j = i + 1; j < len; j++) {
				if (nos.get(i).equals(nos.get(j))) {
					return false;
				}
			}
		}
		return true;
	}
}

/**
 * PK10单式验证器
 * @author Lane
 *
 */
class PKDSValidator implements Validator {
	@Override
	public boolean validate(String nums, int length) {
		List<String> nos = StringUtils.partition(nums, 2);
		int len = nos.size();
		for (int i = 0; i < len; i++) {
			if (Integer.parseInt(nos.get(i)) > 10 || Integer.parseInt(nos.get(i)) < 1) {
				return false;
			}
			for (int j = i + 1; j < len; j++) {
				if (nos.get(i).equals(nos.get(j))) {
					return false;
				}
			}
		}
		return true;
	}
}
